﻿import typing
import sys
import os.path
import chardet
import traceback
import inspect
from datetime import datetime


def getSysExcInfoErrorMsg() -> str:
	"""
	获取因异常诞生的信息
	:return: 异常信息
	"""
	try:
		exc_type, exc_value, exc_traceback = sys.exc_info()
		errorList = traceback.format_tb(exc_traceback)
		writeContent = "\n"
		if errorList is not None and len(errorList) != 0:
			for item in errorList:
				writeContent = f"{writeContent}{item}"

		writeContent = f"\n===========\n错误类型:{exc_type}\n错误信息:{exc_value}\n错误堆栈:{writeContent}\n===========\n"
		return writeContent
	except:
		traceback.print_exc()
		return "error getSysExcInfoErrorMsg"


def getLogPath() -> str:
	"""
	获取写入日志的路径
	"""
	return f"{os.path.dirname(inspect.stack()[1].filename)}{os.sep}log"


def findUrlName(HttpUrl: str) -> str:
	"""
	返回一个不带 http 或 https 与相关参数的网站 url
	:param HttpUrl: 检测的 url
	:return: 一个标准的 url，除非用户输入的 rul 异常
	"""
	## 开始位置
	startFindIndex = HttpUrl.rfind(":")
	if startFindIndex == -1:
		return HttpUrl
	startFindIndex = HttpUrl.find("//", startFindIndex)
	## 去掉开始的 "//"
	result = HttpUrl[startFindIndex + 2:]
	startFindIndex = result.find("/")
	if startFindIndex == -1:
		return result
	## 去掉末尾的 "/*."
	result = result[0:startFindIndex]
	return result


def writeLogFile(content: str, httpUrl: str = "None", typeStr: str = "normal", path: str = None):
	"""
	写入一个错误日志
	:param content: 写入内容
	:param httpUrl: url
	:param requestResponse: 请求头
	:param typeStr: 类型，影响存储文件路径
	:param path: 所在路径，不填写为默认
	"""
	try:
		nowTimeObj = datetime.now()
		nowTimeStr = nowTimeObj.strftime('%Y 年 %m 月 %d 日 %H 时 %M 分 %S 秒 .%f')
		content = f"\n<!-- {nowTimeStr} : -->\n{content}\n"
		nowTimeStr = nowTimeObj.strftime('%Y_%m_%d_%H_%M_%S_%f')
		if path is None:
			## 写入异常文件
			path = f"{getLogPath()}/{findUrlName(httpUrl)}/{typeStr}/{nowTimeStr}_error.html"
		writeFile(content, path)
	except:
		sys.stderr.write(f"\t写入文件异常: {getSysExcInfoErrorMsg()}\n")


def mkDirs(path: str) -> bool:
	"""
	创建目录
	:param path: 目录
	"""
	try:
		path = os.path.realpath(path)
		path = path.replace("\\", os.path.sep).replace("/", os.path.sep)
		split = path.split(os.path.sep)
		dirPathCount = len(split)
		nowDirName = split[0]
		if not os.path.exists(nowDirName):
			raise IOError(f"({path})not has diect {nowDirName}")
		for index in range(1, dirPathCount):
			nowDirName = nowDirName + os.path.sep + split[index]
			if not os.path.exists(nowDirName):
				try:
					os.mkdir(nowDirName)
				except:
					error_msg = f"{getSysExcInfoErrorMsg()}"
					sys.stderr.write(error_msg)
					writeLogFile(error_msg, "mkdir error")
		return True
	except:
		msg = getSysExcInfoErrorMsg()
		sys.stderr.write(msg)
		writeLogFile(msg)
		return False


def initParamArgs(argvDict: typing.Dict[typing.AnyStr, typing.List[typing.AnyStr]] = None) -> typing.Dict[typing.AnyStr, typing.List[typing.AnyStr]]:
	"""
	使用命令行参数添加到映射列表中
	:param argvDict: 添加对象
	:return: 成功返回对象，失败返回 none
	"""
	argLen = len(sys.argv)

	index = 1
	if argvDict is None:
		argvDict = {}
	if argLen < 2:
		return argvDict
	argvList: typing.List[typing.AnyStr] = []
	subArgStr: typing.AnyStr = sys.argv[index]
	while index < argLen:
		argvList = []
		# 选项
		argStr: typing.AnyStr = sys.argv[index]
		if argStr[0] == '-':
			argStr = argStr[1:]
			index += 1
			while index != argLen:
				subArgStr = sys.argv[index]
				if subArgStr[0] == '-':
					break
				argvList.append(subArgStr)
				index += 1
			if argvDict.get(argStr) is None:
				argvDict[argStr] = argvList
			else:
				for argItem in argvList:
					argvDict[argStr].append(argItem)
		else:
			while index != argLen:
				subArgStr = sys.argv[index]
				if subArgStr[0] == '-':
					break
				argvList.append(subArgStr)
				index += 1
			if argvDict.get("") is None:
				argvDict[""] = argvList
			else:
				for argItem in argvList:
					argvDict[""].append(argItem)
	return argvDict


def getPathAbsName(filePath: str) -> str:
	"""
	返回一个绝对路径
	@param filePath: 路径
	@return: 绝对路径
	"""
	return os.path.normpath(os.path.abspath(filePath))


def readFile(readFilePath: str, mode: str = "r", encoding="utf-8", errors='ignore') -> typing.Union[str, None]:
	"""
	读取文件, 如果成功返回内容, 失败或内容为空则返回 None
	:param readFilePath: 文件路径
	:param mode: 文件打开类型
	:param encoding: 编码方式
	:param errors: 错误
	:return: 读取内容, 失败或内容为空则返回 空字符串
	"""
	fileContents: str = ""
	if readFilePath is None or not os.path.exists(readFilePath):
		return fileContents
	if encoding is None:
		try:
			with open(readFilePath, "rb") as file:
				encoding = chardet.detect(file.read())["encoding"]
		except:
			msg = getSysExcInfoErrorMsg()
			writeLogFile(msg, "chardet.detect")
			sys.stderr.write(msg)
	try:
		with open(readFilePath, mode, encoding=encoding, errors=errors) as file:
			while True:
				buffStr = file.read()
				if buffStr is None or len(buffStr) == 0:
					break
				fileContents += buffStr
	except:
		msg = getSysExcInfoErrorMsg()
		writeLogFile(msg, "chardet.detect")
		sys.stderr.write(msg)
	return fileContents


def writeFile(contents, path, mode="w", encoding="utf-8", clear: bool = True) -> bool:
	"""
	写入文件
	:param contents: 内容
	:param path: 文件路径
	:param mode: 写入模式
	:param encoding: 写入编码
	:param clear: 是否清理内容
	:param appendStarStr: 追加在 conten 前的字符串
	:param appendLastStr: 追加在 conten 后的字符串
	"""
	try:
		fileAbspath = os.path.abspath(path)
		if not os.path.exists(path):
			# 检测目录
			dirPath = os.path.split(fileAbspath)[0]
			if not os.path.exists(dirPath):
				mkDirs(dirPath)
		if clear and mode.find("a") == -1:
			with open(path, mode, encoding=encoding) as file:
				file.seek(0)
				file.truncate()
				file.write(contents)
		else:
			with open(path, mode, encoding=encoding) as file:
				file.write(contents)
		return True
	except:
		msg = f"{getSysExcInfoErrorMsg()}\n==>\n{path}"
		sys.stderr.write(msg)
		writeLogFile(msg, httpUrl="write readFilePath error")
		return False


def getPathName(filePath: str) -> str:
	"""
	返回带后缀的文件名称，若为目录，则返回目录名称
	@param filePath: 路径
	@return: 字符串
	"""
	dirPath = os.path.abspath(filePath)
	return dirPath.split(os.sep)[-1]


def getTargetAbsSavePath(file: str) -> str:
	"""
	返回文件所在的绝对路径，若它作为文件夹，则返回文件夹的父级绝对路径
	:param file: 检测而的文件路径
	:return: 路径
	"""
	realpath = os.path.realpath(file)
	if os.path.isdir(realpath):
		return realpath
	return os.path.dirname(realpath)


def strRemoveSpaces(srcStr: str) -> str:
	"""
	删除字符串当中所有的空格
	:param srcStr: 源字符串
	:return: 被移除所有空格的字符串
	"""
	result: list = []

	for strSub in srcStr:
		if strSub.isspace() or strSub == "\ufeff" or strSub == "":
			continue
		result.append(strSub)
	return "".join(result)


def getPathFilesNames(path: str, inDirPath: bool = True) -> list[str]:
	"""
	获取路径下的所有文件
	@param path: 路径
	@param inDirPath: 是否递归
	@return: 所有文件
	"""
	result: list[str] = []
	if not os.path.exists(path):
		return result
	if os.path.isfile(path):
		fileAllPath = os.path.abspath(path)
		result.append(fileAllPath)
	else:
		dirName = None
		for root, dirs, files in os.walk(path, topdown=inDirPath):
			for file in files:
				fileAllPath = os.path.abspath(f"{root}{os.sep}{file}")
				fileAllPath = os.path.normpath(fileAllPath)
				result.append(fileAllPath)
	return result
